package org.testfire.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.xerces.dom.CoreDocumentImpl;
import org.w3c.dom.*;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.*;
import org.w3c.dom.traversal.NodeFilter;

/**
 * @author Geoff Granum
 * @version 1.0
 */

public class DOM3XmlReader implements LSParserFilter
{

    public DOM3XmlReader()
    {
    }

    /**
     * This convenience method takes a file and opens it into a DOM structure. The method creates a <code>Document</code>
     * from the file which is then returned. Calling this method is the equivilent to <br>
     * <code>getDocument(xmlFile, schemaSource, true, true);</code>
     * @param xmlFile The file to parse.
     * @param schemaSource The array of Schema source locations -- Path strings cast down to objects.
     * @return A Document object which represents the XML file passed as an argument.
     * @param resolvers An array of DOM3XmlResolvers.
     */
    public static Document getDocument( File xmlFile, Object[] schemaSource, SchemaResolver[] resolvers )
    {
        return getDocument( xmlFile, schemaSource, true, resolvers );
    }

    /**
     * This convenience method takes a file and opens it into a DOM structure. The method creates a <code>Document</code>
     * from the file which is then returned.
     * @param xmlFile The file to parse.
     * @param schemaSource The array of Schema source locations -- Path strings cast down to objects.
     * @param validating as defined in DcumentBuilderFacotory.setValidating(boolean);
     * @return A Document object which represents the XML file passed as an argument.
     * @param resolvers An array of DOM3XmlReader.SchemaResolvers.
     */
    public static Document getDocument( File xmlFile, Object[] schemaSource,
        boolean validating, final SchemaResolver[] resolvers )
    {

        CoreDocumentImpl document = null;
        try
        {
// get DOM Implementation using DOM Registry
            System.setProperty( DOMImplementationRegistry.PROPERTY,
                "org.apache.xerces.dom.DOMImplementationSourceImpl" );
            DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();

            final DOMImplementationLS impl = (DOMImplementationLS)registry.getDOMImplementation( "LS" );

            // create DOMBuilder
            LSParser builder = impl.createLSParser( DOMImplementationLS.MODE_SYNCHRONOUS, null );

            DOMConfiguration config = builder.getDomConfig();

            // create Error Handler
            DOMErrorHandler errorHandler = new XMLErrorHandler();

            // create filter
            LSParserFilter filter = new DOM3XmlReader();

            builder.setFilter( filter );

            // set error handler
            config.setParameter( "error-handler", errorHandler );

            // set validation feature
            if ( validating )
            {
                config.setParameter( "validate", Boolean.TRUE );
            }
            else
            {
                config.setParameter( "validate", Boolean.FALSE );
            }

            // set schema language
            config.setParameter( "schema-type", "http://www.w3.org/2001/XMLSchema" );

            // set schema location
            StringBuffer schemas = new StringBuffer();

            if ( schemaSource != null )
            {
                for ( Object aSchemaSource : schemaSource )
                {
                    schemas.append( aSchemaSource );
                }
            }
            // schemas.deleteCharAt(schemas.length() - 1);

            config.setParameter( "schema-location", schemas.toString() );

            // parse document
            document = (CoreDocumentImpl)builder.parseURI( xmlFile.getAbsolutePath() );

            // set error handler on the Document
            config = document.getDomConfig();

            config.setParameter( "error-handler", errorHandler );

            // set validation feature
            if ( validating )
            {
                config.setParameter( "validate", Boolean.TRUE );
            }
            else
            {
                config.setParameter( "validate", Boolean.FALSE );
            }

            config.setParameter( "schema-type", "http://www.w3.org/2001/XMLSchema" );
            config.setParameter( "schema-location", schemas.toString() );

            config.setParameter( "resource-resolver", new LSResourceResolver()
            {
                public LSInput resolveResource( String type, String namespaceURI, String publicId, String systemId,
                    String baseURI )
                {
                    for ( SchemaResolver resolver : resolvers )
                    {
                        if ( systemId != null && systemId.equals( resolver.ifISayThis ) )
                        {
                            LSInput ret = impl.createLSInput();
                            FileInputStream is = null;
                            try
                            {
                                is = new FileInputStream( resolver.iMeanThat );
                            }
                            catch ( IOException e )
                            {
                                e.printStackTrace();
                                System.exit( 1 );
                            }
                            ret.setByteStream( is );
                            ret.setSystemId( systemId );
                            return ret;
                        }
                    }
                    return null;
                }
            }
            );

            // remove comments from the document
//	  config.setParameter("comments", Boolean.FALSE);
            document.normalizeDocument();

        }
        catch ( ClassNotFoundException ex )
        {
            ex.printStackTrace();
        }
        catch ( InstantiationException ex )
        {
            ex.printStackTrace();
        }
        catch ( IllegalAccessException ex )
        {
            ex.printStackTrace();
        }
        catch ( ClassCastException ex )
        {
            ex.printStackTrace();
        }

        return document;
    }

    public short acceptNode( Node enode )
    {
        return NodeFilter.FILTER_ACCEPT;
    }

    public int getWhatToShow()
    {
        return NodeFilter.SHOW_ELEMENT;
    }

    public short startElement( Element elt )
    {
        return LSParserFilter.FILTER_ACCEPT;
    }

    public static class SchemaResolver
    {
        private final String ifISayThis;

        private final File iMeanThat;

        public SchemaResolver( String ifISayThis, File iMeanThat )
        {
            this.ifISayThis = ifISayThis;
            this.iMeanThat = iMeanThat;
        }
    }


}